package msu.ml.tools;

import msu.ml.data.*;
import msu.ml.data.level2.*;
import msu.ml.presentation.*;
import msu.ml.util.*;

public class GooseUploader
{
   private final double D2R = 0.01745;
   private final double R2D = 57.29577;
   private final double ERAD = 6378.135;

   public static void main(String [] args)
   {
      String syntax = "{w$window} {v$visualize-errors} {s$second-order-moments} $LAT1 $LON1 $LAT2 $LON2 $CLASS $FILE";
      CommandLineParser parser = null;

      try
      {
         parser = new CommandLineParser(syntax);
         parser.parse(args);
      }
      catch(Exception e)
      {
         System.out.println(e.getMessage());
         e.printStackTrace();
         System.exit(0);
      }

      double radarLat = parser.getOption("LAT1").getValueAsDouble();
      double radarLon = parser.getOption("LON1").getValueAsDouble();
      double targetLat = parser.getOption("LAT2").getValueAsDouble();
      double targetLon = parser.getOption("LON2").getValueAsDouble();

      int classification = parser.getOption("CLASS").getValueAsInt();

      String file = parser.getOption("FILE").value;

      GooseUploader g = new GooseUploader();

      int range = (int)(g.distance(radarLat, radarLon, targetLat, targetLon));
      double az = g.azimuth(radarLat, radarLon, targetLat, targetLon);

      VolumeReader reader = new VolumeReader(new BlockReader(file));
      Volume v = reader.readVolume();

      Scan ref = v.getSweep(0).getScan(0);
      int rayIndex = 0;
      while(ref.getRay(rayIndex + 1).getAzimuth() < az)
         rayIndex++;

      double rvalue = ref.getRay(rayIndex).getReflectivityAtRange(range);

      //Search Window
      if(rvalue == Ray.BADVAL && parser.hasOption("window"))
      {
         int window = parser.getOption("window").getValueAsInt();
         if(window % 2 == 0)
            window++;

         double [][] values = new double[window][window];

         int newRange = -1;
         double newAz = 0.0;

         for(int j = 0; j < window; j++)
         {
            Ray temp = ref.getRay(rayIndex - (window / 2) + j);
            int rangeIndex = temp.getReflectivityRangeIndex(range);
            for(int i = 0; i < window; i++)
            {
               values[j][i] = temp.getReflectivity(rangeIndex - (window / 2) + i);
               if(values[j][i] != Ray.EMPTY && newRange < 0)
               {
                  if(rvalue == Ray.EMPTY || values[j][i] > rvalue)
                     rvalue = values[j][i];

                  newAz = temp.getAzimuth();
                  newRange = i * temp.getReflectivityGateSize();
               }
            }
         }

         if(parser.hasOption("visualize-errors"))
         {
            ScanRenderer.saveWindow(values, file + "_errorwindow.png",
               200, 200);
         }

         if(newRange > 0)
         {
            az = newAz;
            range = newRange;
         }
      }

      Scan dop = v.getSweep(0).getScan(1);
      rayIndex = 0;
      while(dop.getRay(rayIndex + 1).getAzimuth() < az)
         rayIndex++;

      double vvalue = dop.getRay(rayIndex).getVelocityAtRange(range);
      double svalue = dop.getRay(rayIndex).getSpectrumWidthAtRange(range);

      vvalue = (vvalue == Ray.BADVAL || vvalue == Ray.RFVAL) ? 0.0 : vvalue;
      svalue = (svalue == Ray.BADVAL || svalue == Ray.RFVAL) ? 0.0 : svalue;

      System.out.println(String.format("Range: %1$d, Azimuth: %2$.2f", range, az));

      if(rvalue != Ray.BADVAL)
      {
         System.out.println(String.format("%1$.2f, %2$.2f, %3$.2f, %4$d",
            rvalue, vvalue, svalue, classification));

         DatabaseModel database = new DatabaseModel("jdbc:mysql://localhost/geese", "rmead", "");

         String volumeLabel = file.substring(file.lastIndexOf('/') + 1);

         int volumeId = database.insertVolume(volumeLabel);
         int sweepId = database.insertSweep(volumeId, "0");

         String query = String.format("INSERT INTO pulse_volumes (pulse_id, sweep_id, scan_id, azimuth, range, reflectivity, radial_velocity, spectrum_width, class) VALUES (NULL, %1$d, NULL, %2$.2f, %3$d, %4$.3f, %5$.3f, %6$.3f, %7$d);", sweepId, az, range, rvalue, vvalue, svalue, classification);

         database.updateQuery(query, true);
      }
      else
      {
         System.out.println(String.format(
            "WINDOW ERROR: %1$s Pulse Volume Window Empty [class = %2$d]",
            file, classification));

         if(parser.hasOption("visualize-errors"))
         {
            ScanRenderer.saveErrorScan(ref, file+"_error_scan.png", 1000, 1000, range, az);
         }
      }
   }

   protected double distance(double lat1, double lon1, double lat2, double lon2)
   {
      return gcDistance(lat1, lon1, lat2, lon2);
   }

   //meters
   protected double gcDistance(double lat1, double lon1, double lat2, double lon2)
   {
      lat1 *= D2R;
      lon1 *= D2R;
      lat2 *= D2R;
      lon2 *= D2R;

      double d = Math.sin(lat1) * Math.sin(lat2) +
         Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon1 - lon2);

      return ERAD * Math.acos(d) * 1000.0;
   }

   protected double azimuth(double lat1, double lon1,
      double lat2, double lon2)
   {
      double result = 0.0;

      int ilat1 = (int)(0.5 + lat1 * 360000.0);
      int ilat2 = (int)(0.5 + lat2 * 360000.0);
      int ilon1 = (int)(0.5 + lon1 * 360000.0);
      int ilon2 = (int)(0.5 + lon2 * 360000.0);

      lat1 *= D2R;
      lon1 *= D2R;
      lat2 *= D2R;
      lon2 *= D2R;

      if((ilat1 == ilat2) && (ilon1 == ilon2))
      {
         return result;
      }
      else if(ilat1 == ilat2)
      {
         if(ilon1 > ilon2)
            result = 90.0;
         else
            result = 270.0;
      }
      else if(ilon1 == ilon2)
      {
         if(ilat1 > ilat2)
            result = 180.0;
      }
      else
      {
         double c = Math.acos(Math.sin(lat2) * Math.sin(lat1) +
            Math.cos(lat2) * Math.cos(lat1) * 
            Math.cos(lon2 - lon1));
         double a = Math.asin(Math.cos(lat2) 
            * Math.sin(lon2 - lon1) / Math.sin(c));

         result = a * R2D;
      
         if((ilat2 > ilat1) && (ilon2 > ilon1))
         {

         }
         else if((ilat2 < ilat1) && (ilon2 < ilon1))
         {
            result = 180.0 - result;
         }
         else if((ilat2 < ilat1) && (ilon2 > ilon1))
         {
            result = 180.0 - result;
         }
         else if((ilat2 > ilat1) && (ilon2 < ilon1))
         {
            result += 360.0;
         }
      }

      return result;
   }
}
